package com.fwj;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

import com.fwj.config.Constants;
import com.fwj.config.F5ServicesConfig;
import com.fwj.exception.EsbSynException;
import com.fwj.pojo.ServiceInfo;
import com.fwj.utils.EsbsynUtil;
import com.fwj.utils.DataMigration;
import com.fwj.utils.FormatUtil;
import com.fwj.vo.SftpVo;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;
import org.apache.log4j.Logger;

/**
 * 浙江电信ESB2.0系统和F5设备数据同步
 *
 * @author fwj
 * @data 2015-12-6 10:51:52
 */
public class SynMain {

    private static Logger logger = Logger.getLogger(SynMain.class);
    private static String failureService = "";
    public static List<String> data_toF5Name = new ArrayList<String>();
    public static String nl_windows = "\r\n";//windows 换行符
    private static String nl_linux = "\n";//linux 换行符
    public  static List<SftpVo> stfpVoList = new ArrayList<SftpVo>();
    private static List<Map<String, List>>   syndatalist = new ArrayList<Map<String, List>>();
    public static boolean dubbo_status=false;  //是否已经生成dubbo汇总文件。
    public static void main(String[] args)  {

            //执行前先测试连接。
      boolean status =   testConnect();
      if(!status)
        throw new EsbSynException("F5服务器连接失败！..");
        //拉取f5服务器配置到本机.
        // 1获取服务器配置.
        if(stfpVoList==null||stfpVoList.isEmpty()){
            testConnect();
        }
        SynMain sm = new SynMain();
        for(int i =0;i<stfpVoList.size();i++){

            SftpVo sftpVo =stfpVoList.get(i);

            ServiceInfo serviceInfo = sftpVo.getService_info();
            String ip = serviceInfo.getIp();
            //  执行前先迁移sh文件.
            DataMigration.dataToHis(Constants.sh_data +ip,Constants.sh_his +ip);
            //需要配置f5拉取的路径.
           String shString = sm.esbsyn(sftpVo.local_path);

            String shPath = sm.writeFile(shString,getF5Path(ip));
        //数据同步到配置的服务器中.如果已经存在相同的文件,则覆盖.
                if(shPath!=null) {
                    sftpVo.putFileToSftpServer(shPath, serviceInfo.getUploadPath());
                }
            sftpVo.closeResource();
        }

        //将处理的文件放置历史文件夹
        DataMigration.dataFileToHis(data_toF5Name,Constants.syn_datafile_his);

    }
    private static String getF5Path(String ip){

        //sb生成脚本文件
        String sh_data=Constants.sh_data;
        boolean split =  sh_data.lastIndexOf("/")==sh_data.length()-1;
        sh_data = sh_data + (split==false?"/":"")+ip+"/";
        return sh_data;
    }
    //检查连接是否正常。
    public static boolean testConnect() {
        logger.info("正在读取F5服务器数据....");
        List<ServiceInfo> serviceInfoList = F5ServicesConfig.getInstance();
        if(serviceInfoList==null&&serviceInfoList.size()==0) {
            logger.error("没有读取到服务器数据...");
            throw new EsbSynException("F5服务器数据为空..");
        }
        for(ServiceInfo serviceInfo : serviceInfoList){

            SftpVo vo = new SftpVo();

            Channel channel = vo.getChannel(serviceInfo);
            if(channel!=null&&!channel.isConnected()) {
                failureService=serviceInfo.getIp();
                return false;
            }
            stfpVoList.add(vo);

        }
        return true;
    }

    /**
     * 程序入口
     *
     * @author fwj
     */
    public String esbsyn(String f5ConfigPath) {

        String fileContent = EsbsynUtil.readToString(f5ConfigPath);//读取F5配置文件

        /*************************************
         * 第一步
         * 获取文件，检测文件格式和数据格式合法性
         ************************************/

        //获取数据同步文件List

        if(syndatalist==null||syndatalist.size()==0){
              syndatalist = FormatUtil.formatSynDataFile();
        }
        List serviceCodeList = FormatUtil.formatServiceCode(fileContent);

        List poolList = FormatUtil.formatPools(fileContent);

        logger.info("F5配置文件service-code共计" + serviceCodeList.size() + "个");
        logger.info("F5配置文件Pools共计" + poolList.size() + "组");

        int all_servicecode = 0;//service-code总数
        int exist_servicecode = 0;//service-code存在数据
        int noexist_servicecode = 0;//service-code不存在数

        int all_pool = 0;
        int exist_pool = 0;
//		int add_pool = 0;

        StringBuffer sb = new StringBuffer();

        int cj1 = 0;
        int cj2 = 0;
        int cj3 = 0;
        int cj4 = 0;
        int cj5 = 0;
        int xl = 0;

        for (int i = 0; i < syndatalist.size(); i++) {
            Map map = (Map) syndatalist.get(i);
            String servicecode = (String) map.get("servicecode");
            List ipList = (List) map.get("ipList");

            all_servicecode = syndatalist.size();
            all_pool = syndatalist.size();
            /*************************************
             * 检索Service-code和Pools对应关系
             * 输出日志：Service-code共计*个，新增*个、更新*个、已存在*个；
             ************************************/
            boolean ifservicecode = false;
            for (int j = 0; j < serviceCodeList.size(); j++) {
                Map smap = (Map) serviceCodeList.get(j);
                String sc = (String) smap.get("name");
                if (servicecode.equals(sc)) {
                    ifservicecode = true;
                    exist_servicecode++;
                    break;
                } else {
                    ifservicecode = false;
                    continue;
                }
            }

            noexist_servicecode = all_servicecode - exist_servicecode;

            /*************************************
             * 检索IP成员和Pools对应关系
             * 输出日志：
             * IP成员共计*组，已存在Pools的*组（指出对应关系）
             * 需新增的Pools的*组（列出对应关系清单）
             ************************************/
            boolean ipbool = false;
            String poolName = "";
            //检查pools是否存在
            for (int j = 0; j < poolList.size(); j++) {
                Map poolMap = (Map) poolList.get(j);
                List p_ipList = (List) poolMap.get("ip");
                if (EsbsynUtil.compare(ipList, p_ipList)) {
                    poolName = (String) poolMap.get("pool");
                    ipbool = true;
                    exist_pool++;
                }
            }
//			add_pool = all_pool - exist_pool;
            /*************************************
             * 第四步
             * 生成F5可执行脚本（注意先后循序）
             ************************************/

            String code = servicecode.split("\\.")[1];
            char[] ary = {'0', '0'};
            //场景一：service-code不存在，IP字段值与Pools全匹配；1-DataGroup添加一条数据；
            if (!ifservicecode && ipbool) {
                cj1++;
                sb.append("tmsh modify ltm data-group internal /Common/service-code records add { " + servicecode + " { data " + poolName + " } }" + nl_linux);
            }
            String ipstr = "";
            for (int j = 0; j < ipList.size(); j++) {
                ipstr += (String) ipList.get(j) + " ";
            }
            //场景二：service-code不存在，IP字段值与Pools非全匹配；1-新增Pools，2-DataGroup添加一条数据；
            if (!ifservicecode && !ipbool) {
                cj2++;
                if (sb.indexOf("add { " + ipstr + "} ") <= -1) {//不存在相同的add pool ipstr
                    xl++;
                    Date date = new Date();
                    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddhhmmss");
                    String num = sf.format(date)+EsbsynUtil.random(); //添加五位随机数
                    //String num = getNum(xl,ary);
                    sb.append("tmsh create ltm pool /Common/Dubbo_" + code + "_" + num + "_pl members add { " + ipstr + "} monitor tcp_half_open" + nl_linux);
                    sb.append("tmsh modify ltm data-group internal /Common/service-code records add { " + servicecode + " { data Dubbo_" + code + "_" + num + "_pl } }" + nl_linux);
                } else {//存在相同的add pool，截取之前的pool名称
                    int end = sb.indexOf("add { " + ipstr + "} ");
                    String pool = sb.substring(end - 40, end - 9);
                    sb.append("tmsh modify ltm data-group internal /Common/service-code records add { " + servicecode + " { data " + pool + " } }" + nl_linux);
                }

            }
            //场景三：service-code已存在，IP字段值与Pools非全匹配；1-新增Pools，2-DataGroup修改一条数据；
            if (ifservicecode && !ipbool) {
                cj3++;
                if (sb.indexOf("add { " + ipstr + "} ") <= -1) {//不存在相同的add pool ipstr
                    xl++;
                    //String num = getNum(xl,ary);
                    Date date = new Date();
                    SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddhhmmss");
                    String num = sf.format(date)+EsbsynUtil.random();
                    sb.append("tmsh create ltm pool /Common/Dubbo_" + code + "_" + num + "_pl members add { " + ipstr + "} monitor tcp_half_open" + nl_linux);
                    sb.append("tmsh modify ltm data-group internal /Common/service-code records modify { " + servicecode + " { data Dubbo_" + code + "_" + num + "_pl } }" + nl_linux);
                } else {
                    int end = sb.indexOf("add { " + ipstr + "} ");
                    String pool = sb.substring(end - 40, end - 9);
                    sb.append("tmsh modify ltm data-group internal /Common/service-code records modify { " + servicecode + " { data " + pool + " } }" + nl_linux);
                }
            }


            if (ifservicecode && ipbool) {


                for (int j = 0; j < serviceCodeList.size(); j++) {
                    Map smap = (Map) serviceCodeList.get(j);
                    String sc = (String) smap.get("name");
                    String pn = (String) smap.get("data");
                    if (servicecode.equals(sc)) {
                        //场景五：service-code已存在，IP字段值与Pools全匹配，并且两者对应关系不变；无操作；
                        if (pn.equals(poolName)) {
                            cj5++;
                        } else if (!pn.equals(poolName)) {
                            //场景四：service-code已存在，IP字段值与Pools全匹配，但是两者对应关系有变化；1-DataGroup修改一条数据；
                            cj4++;
                            sb.append("tmsh modify ltm data-group internal /Common/service-code records modify { " + servicecode + " { data " + poolName + " } }" + nl_linux);
                        }
                    }

                }

            }


        }

        //对sb重新排序，add pool 排列在前
        String[] sbs = null;
        if (sb.toString().indexOf(nl_linux) > -1) {
            sbs = sb.toString().split(nl_linux);
        } else {
            sbs = sb.toString().split(nl_windows);
        }

        List first = new ArrayList();
        List second = new ArrayList();
        for (int i = 0; i < sbs.length; i++) {
            String str = sbs[i];
            if (str.indexOf("tmsh create ltm pool") > -1 && str.indexOf("add") > -1) {
                first.add(str);
            } else {
                second.add(str);
            }
        }
        first.addAll(second);
        StringBuffer newSb = new StringBuffer();
        for (int i = 0; i < first.size(); i++) {
            String str = (String) first.get(i);
            newSb.append(str + nl_linux);
        }

        //保存配置，以防设备重启丢失
        newSb.append("tmsh save /sys config");

        logger.info("数据同步文件IP成员Pools共计" + all_pool + "组，已存在" + exist_pool + "组");
        logger.info("数据同步文件Service-code共计" + all_servicecode + "个，已存在" + exist_servicecode + "个，不存在" + noexist_servicecode + "个");

        logger.info("场景一：service-code不存在，IP字段值与Pools全匹配；1-DataGroup添加一条数据；共计： " + cj1 + " 条");
        logger.info("场景二：service-code不存在，IP字段值与Pools非全匹配；1-新增Pools，2-DataGroup添加一条数据；共计： " + cj2 + " 条");
        logger.info("场景三：service-code已存在，IP字段值与Pools非全匹配；1-新增Pools，2-DataGroup修改一条数据；共计： " + cj3 + " 条");
        logger.info("场景四：service-code已存在，IP字段值与Pools全匹配，但是两者对应关系有变化；1-DataGroup修改一条数据；；共计： " + cj4 + " 条");
        logger.info("场景五：service-code已存在，IP字段值与Pools全匹配，并且两者对应关系不变；无操作；共计： " + cj5 + " 条");

        return newSb.toString();

    }


    /**
     * 写入脚本文件
     *
     * @param str
     */
    public String writeFile(String str, String path) {

        //如果写入的文件内容为空，则直接返回
        if(str==null||"".equals(str.trim())||"tmsh save /sys config".equals(str.trim())){
            logger.info("文件为空,无需写入.");
            return null;
        }
        String shPath ="";
        FileOutputStream fos = null;
        Date date = new Date();
        SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddhhmmss");
//		String newFilename = getFileName(path, 1);
        String newFilename = "F5_" + sf.format(date) + ".sh";
        try {
            logger.info("写入路径"+path);
          //  File file = new File(path + newFilename);
            File file = new File(path);
            if(!file.exists()){
                file.mkdir();
            }
            File filePath = new File(path + newFilename);
            if (!filePath.exists())
            filePath.createNewFile();

            byte bytes[] = new byte[512];
            bytes = str.getBytes();   //新加的
            int b = str.length();   //改
            fos = new FileOutputStream(filePath);
            fos.write(bytes, 0, b);
            logger.info("已生成F5可执行脚本文件：" + path + newFilename);
            shPath = path + newFilename;
        } catch (IOException e) {
            e.printStackTrace();
            throw new EsbSynException("写入脚本文件失败!");
        } finally {
            if (null != fos) {
                try {
                    fos.close();

                } catch (IOException e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
        }
        return shPath;
    }





}
